package com.heiye.blog.ai.config;

import com.heiye.blog.ai.enums.ResponseCodeEnum;
import com.heiye.blog.framework.common.exception.BizException;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.util.retry.Retry;

import java.io.IOException;
import java.time.Duration;

/**
 * @author: heiye
 * @date: 2025/11/11 下午3:12
 * @version: v1.0.0
 * @description: WebClientConfig
 */
@Slf4j
@Configuration
public class WebClientConfig {
    @Bean
    public WebClient webClient() {
        // 配置HTTP连接池
        ConnectionProvider provider = ConnectionProvider.builder("ai")
                // 配置最大连接数 500
                .maxConnections(500)
                // 配置空闲连接超时时间 30s
                .maxIdleTime(Duration.ofSeconds(30))
                // 配置连接生命周期时间 10 min
                .maxLifeTime(Duration.ofMinutes(10))
                // 获取连接池中连接的最大超时时间 10s
                .pendingAcquireTimeout(Duration.ofSeconds(10))
                // 不需要频繁的自动清理 2 min
                .evictInBackground(Duration.ofMinutes(2))
                .build();

        // 配置HTTP客户端
        HttpClient httpClient = HttpClient.create(provider)
                // 连接超时 10s
//                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                // 响应超时时间 60s
                .responseTimeout(Duration.ofSeconds(60))
                .doOnConnected(conn ->
                        // 添加数据读取超时处理 60s
                        // ai 流式处理可能较慢，同时部分模型支持深度思考却无法显示推理过程
                        conn.addHandlerLast(new ReadTimeoutHandler(60))
                                // 添加发送请求数据写入超时处理 10s
                                .addHandlerLast(new WriteTimeoutHandler(10)));


        // 构建WebClient实例
        return WebClient.builder()
                // 设置 HTTP 客户端
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                // 添加默认请求头
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                // 添加请求日志记录功能
                .filter(ExchangeFilterFunction.ofRequestProcessor(
                        clientRequest -> {
                            log.info("====== 发送请求: 请求类型：{}, 请求地址：{}, 请求参数：{}",
                                    clientRequest.method(),
                                    clientRequest.url(),
                                    clientRequest.body());
                            return Mono.just(clientRequest);
                        }
                ))
                // 添加响应日志记录功能
                .filter(ExchangeFilterFunction.ofResponseProcessor(
                        clientResponse -> {
                            log.debug("====== 接收请求: 状态码：{}",
                                    clientResponse.statusCode());
                            return Mono.just(clientResponse);
                        }
                ))
                // 重试机制
                .filter((request, next) -> next.exchange(request)
                        // 添加重试机制 最大重试次数 3 次，每重试一次增加一秒
                        .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
                                .filter(throwable -> {
                                    // 对服务端异常进行重试
                                    if (throwable instanceof WebClientResponseException responseException) {

                                        HttpStatusCode status = responseException.getStatusCode();
                                        return status.is5xxServerError();
                                    }

                                    // 对网络超时等IO异常也重试
                                    return throwable instanceof IOException;
                                })
                                .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
                                    // 重试耗尽后的处理
                                    Throwable finalFailure = retrySignal.failure();
                                    if (finalFailure instanceof WebClientResponseException responseException) {
                                        HttpStatusCode status = responseException.getStatusCode();
                                        // 服务端响应异常处理
                                        if (status.is5xxServerError()) {
                                            log.error("====== 服务端响应异常: 状态码：{}, 响应体：{}", responseException.getStatusCode(), responseException.getResponseBodyAsString());

                                            throw new BizException(ResponseCodeEnum.AI_RESPONSE_TIMEOUT);
                                        }
                                    }
                                    // 对网络超时等IO异常处理报错
                                    else if (finalFailure instanceof IOException) {
                                        log.error("====== 网络超时或IO异常: {}", finalFailure.getMessage());

                                        throw new BizException(ResponseCodeEnum.AI_RESPONSE_TIMEOUT);
                                    }
                                    return finalFailure;
                                })
                        )
                )
                .build();
    }

    @Bean
    public WebClient.Builder webClientBuilder() {
        // 配置HTTP连接池
        ConnectionProvider provider = ConnectionProvider.builder("ai")
                // 配置最大连接数 500
                .maxConnections(500)
                // 配置空闲连接超时时间 30s
                .maxIdleTime(Duration.ofSeconds(30))
                // 配置连接生命周期时间 10 min
                .maxLifeTime(Duration.ofMinutes(10))
                // 获取连接池中连接的最大超时时间 10s
                .pendingAcquireTimeout(Duration.ofSeconds(10))
                // 不需要频繁的自动清理 2 min
                .evictInBackground(Duration.ofMinutes(2))
                .build();

        // 配置HTTP客户端
        HttpClient httpClient = HttpClient.create(provider)
                // 连接超时 10s
//                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                // 响应超时时间 60s
                .responseTimeout(Duration.ofSeconds(60))
                .doOnConnected(conn ->
                        // 添加数据读取超时处理 60s
                        // ai 流式处理可能较慢，同时部分模型支持深度思考却无法显示推理过程
                        conn.addHandlerLast(new ReadTimeoutHandler(60))
                                // 添加发送请求数据写入超时处理 10s
                                .addHandlerLast(new WriteTimeoutHandler(10)));


        // 构建WebClient实例
        return WebClient.builder()
                // 设置 HTTP 客户端
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                // 添加默认请求头
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                // 添加请求日志记录功能
                .filter(ExchangeFilterFunction.ofRequestProcessor(
                        clientRequest -> {
                            log.info("====== 发送请求: 请求类型：{}, 请求地址：{}",
                                    clientRequest.method(),
                                    clientRequest.url());
                            return Mono.just(clientRequest);
                        }
                ))
                // 添加响应日志记录功能
                .filter(ExchangeFilterFunction.ofResponseProcessor(
                        clientResponse -> {
                            log.debug("====== 接收请求: 状态码：{}",
                                    clientResponse.statusCode());
                            return Mono.just(clientResponse);
                        }
                ))
                // 重试机制
                .filter((request, next) -> next.exchange(request)
                        // 添加重试机制 最大重试次数 3 次，每重试一次增加一秒
                        .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
                                .filter(throwable -> {
                                    // 对服务端异常进行重试
                                    if (throwable instanceof WebClientResponseException responseException) {

                                        HttpStatusCode status = responseException.getStatusCode();
                                        return status.is5xxServerError();
                                    }

                                    // 对网络超时等IO异常也重试
                                    return throwable instanceof IOException;
                                })
                                .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
                                    // 重试耗尽后的处理
                                    Throwable finalFailure = retrySignal.failure();
                                    if (finalFailure instanceof WebClientResponseException responseException) {
                                        HttpStatusCode status = responseException.getStatusCode();
                                        // 服务端响应异常处理
                                        if (status.is5xxServerError()) {
                                            log.error("====== 服务端响应异常: 状态码：{}, 响应体：{}", responseException.getStatusCode(), responseException.getResponseBodyAsString());

                                            throw new BizException(ResponseCodeEnum.AI_RESPONSE_TIMEOUT);
                                        }
                                    }
                                    // 对网络超时等IO异常处理报错
                                    else if (finalFailure instanceof IOException) {
                                        log.error("====== 网络超时或IO异常: {}", finalFailure.getMessage());

                                        throw new BizException(ResponseCodeEnum.AI_RESPONSE_TIMEOUT);
                                    }
                                    return finalFailure;
                                })
                        )
                );
    }
}
